package com.cxhc.common.wechat;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.cxhc.common.config.WechatMiniProgramConfig;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class WechatUtils {

    // API错误码常量
    private static final int SUCCESS_ERR_CODE = 0;
    public static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
    // 获取手机号请求
    private static final String PHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";
    // 授权地址
    private static final String JSCODESESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";

    private static final String URLLINK_URL = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s";


    // 定义类常量，用于提高代码的可维护性
    private static final String APPID_KEY = "appid";
    private static final String SECRET_KEY = "secret";
    private static final String JS_CODE_KEY = "js_code";
    private static final String GRANT_TYPE_KEY = "grant_type";
    private static final String AUTHORIZATION_CODE = "authorization_code";
    private static final String EMPTY_STRING = "openid";


    //通过 URL Link 进入的小程序页面路径，必须是已经发布的小程序存在的页面，不可携带 query 。path 为空时会跳转小程序主页
    private static final String PATH = "path";
    // 携带的参数 a=2
    private static final String QUERY = "query";
    //生成的 URL Link 类型，到期失效：true，永久有效：false
    private static final String IS_EXPIRE = "is_expire";
    //小程序 URL Link 失效类型，失效时间：0，失效间隔天数：1
    private static final String EXPIRE_TYPE = "expire_type";
    //到期失效的URL Link的失效间隔天数。生成的到期失效URL Link在该间隔时间到达前有效。最长间隔天数为365天。expire_type 为 1 必填
    private static final String EXPIRE_INTERVAL = "expire_interval";
    private static final String URL_LINK = "url_link";


    @Resource
    private WechatMiniProgramConfig config;


    /**
     * 获取token
     *
     * @return
     */
    public static String getToken(String appId, String appSecret) {
        if (appId.isEmpty() || appSecret.isEmpty()) {
            throw new IllegalArgumentException("Code and token must not be null.");
        }
        try {
            Map<String, Object> requestParameters = new HashMap<>();
            requestParameters.put("grant_type", "client_credential");
            requestParameters.put("appid", appId);
            requestParameters.put("secret", appSecret);

            JSONObject response = JSONUtil.parseObj(HttpUtil.get(TOKEN_URL, requestParameters));
            String accessToken = response.getStr("access_token");
            if (StringUtils.isEmpty(accessToken)) {
                throw new RuntimeException("Failed to retrieve access token.");
            }

            return accessToken;
        } catch (Exception e) {
            throw new RuntimeException("Exception occurred while trying to get token: " + e.getMessage());
        }
    }


    /**
     * 微信获取手机号码
     *
     * @param code  验证码
     * @param token 安全令牌
     * @return 手机号码
     * @throws AuthenticationException 验证失败时抛出异常
     * @throws IOException             网络请求异常
     */
    public static String authenticatePhoneNumber(String code, String token) throws AuthenticationException, IOException {
        if (code == null || token == null) {
            throw new IllegalArgumentException("Code and token must not be null.");
        }

        Map<String, String> params = new HashMap<>(1); // 初始化容量，减少resize
        params.put("code", code);
        // 执行HTTP POST请求
        ResponseEntity<JSONObject> result = new RestTemplate().postForEntity(String.format(PHONE_URL, token), params, JSONObject.class);

        // 解析响应结果
        int errCode = result.getBody().getInt("errcode");
        String errMsg = result.getBody().getStr("errmsg");

        // 错误处理
        if (errCode != SUCCESS_ERR_CODE) {
            // 抛出更具体的异常，提供更多的错误信息
            throw new AuthenticationException("Authentication failed: " + errMsg);
        }

        // 获取手机号码
        JSONObject phoneInfo = result.getBody().getJSONObject("phone_info");
        String phoneNumber = phoneInfo.getStr("phoneNumber");

        if (phoneNumber == null || phoneNumber.isEmpty()) {
            // 处理结果中不存在手机号码的情况
            throw new AuthenticationException("Phone number not found in the response.");
        }

        return phoneNumber;
    }


    public static String getOpenId(String code, String appId, String secret) throws AuthenticationException {

        if (code == null || code.isEmpty()) {
            throw new IllegalArgumentException("Code cannot be null or empty.");
        }
        Map<String, Object> requestParameters = new HashMap<>();
        requestParameters.put(APPID_KEY, appId);
        requestParameters.put(SECRET_KEY, secret);
        requestParameters.put(JS_CODE_KEY, code);
        requestParameters.put(GRANT_TYPE_KEY, AUTHORIZATION_CODE);
        try {
            String res = HttpUtil.post(JSCODESESSION_URL, requestParameters);
            JSONObject result = JSONUtil.parseObj(res);
            if (!result.isEmpty() && result.containsKey(EMPTY_STRING)) {
                return result.getStr(EMPTY_STRING);
            }
            throw new AuthenticationException("Empty or null response received while fetching openId.");
        } catch (Exception e) {
            throw new AuthenticationException("Exception occurred while fetching openId: " + e.getMessage());
        }
    }


    // 自定义异常类
    public static class AuthenticationException extends Exception {
        public AuthenticationException(String message) {
            super(message);
        }
    }


    /**
     * 生成短链【短信、邮件、网页等】
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    public static String createUrlLink(String token, String path, String query) throws AuthenticationException {
        if (token == null || token.isEmpty()) {
            throw new IllegalArgumentException("token cannot be null or empty.");
        }
        Map<String, Object> requestParameters = new HashMap<>();
        requestParameters.put(PATH, path);
        requestParameters.put(QUERY,query);
        requestParameters.put(IS_EXPIRE, false);
        requestParameters.put(EXPIRE_TYPE, 1);
        requestParameters.put(EXPIRE_INTERVAL, 1);
        try {
            String res = HttpUtil.post(String.format(URLLINK_URL, token), requestParameters);
            JSONObject result = JSONUtil.parseObj(res);
            if (!result.isEmpty() && result.containsKey(URL_LINK)) {
                return result.getStr(URL_LINK);
            }
            throw new AuthenticationException("Empty or null response received while fetching url_link.");
        } catch (Exception e) {
            throw new AuthenticationException("Exception occurred while fetching url_link: " + e.getMessage());
        }
    }
}
